# -*- coding: utf-8 -*-
# @Time    : 2024/7/4 17:23
# @Author  : yujiahao
# @File    : 03_fastapi_path_parameters.py
# @description:路径参数
from enum import Enum

from fastapi import FastAPI

app = FastAPI()


# todo 1、路径参数
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    '''
    1、【路径参数】如果不声明类型的话，FastAPI 支持使用 Python 字符串格式化语法声明路径参数（变量）。\n
    2、【声明路径参数的类型】通过调用 http://127.0.0.1:8000/items/22 这段代码会将传进来的数据限制类型。\n
    3、【数据转换】虽然看起来都是22 ，但是如果加了类型就是int 不是 str。\n
    4、【数据校验】如果匹配将输出  http://127.0.0.1:8000/items/后面的内容，否则报错。\n

        通过浏览器访问 http://127.0.0.1:8000/items/foo，接收如下 HTTP 错误信息：\n

            {
                "detail": [
                    {
                        "loc": [
                            "path",
                            "item_id"
                        ],
                        "msg": "value is not a valid integer",
                        "type": "type_error.integer"
                    }
                ]
            }\n

    5、【查看文档】 访问 http://127.0.0.1:8000/docs，查看自动生成的 API 文档，而且这里的注释也会在文档中显示。\n
    6、【基于标准的好处，备选文档】FastAPI 使用 OpenAPI 生成概图，所以能兼容很多工具。\n

        因此，FastAPI 还内置了 ReDoc 生成的备选 API 文档，可在此查看 http://127.0.0.1:8000/redoc\n
    7、【Pydantic¶】FastAPI 充分地利用了 Pydantic 的优势，用它在后台校验数据。众所周知，Pydantic 擅长的就是数据校验。
        同样，str、float、bool 以及很多复合数据类型都可以使用类型声明。\n
    '''

    return {"item_id": item_id}


# todo 2、顺序很重要
@app.get("/users/me")
async def read_user_me():
    '''
    【顺序很重要】
        - 有时，路径操作中的路径是写死的。\n
        - 比如要使用 /users/me 获取当前用户的数据。\n
        - 然后还要使用 /users/{user_id}，通过用户 ID 获取指定用户的数据。\n
        - 由于路径操作是按顺序依次运行的，因此，一定要在 /users/{user_id} 之前声明 /users/me\n
        - 否则，/users/{user_id} 将匹配 /users/me，FastAPI 会认为正在接收值为 "me" 的 user_id 参数。\n
    '''

    return {"user_id": "i`m yujiahao"}


@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return {"user_id": user_id}


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


# todo 3、预设值

# 路径参数的值是枚举的元素。
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    '''
    【预设值】\n

        路径操作使用 Python 的 Enum 类型接收预设的路径参数。如果传入的参数不在这里面会报错。\n
        API 文档会显示预定义路径参数的可用值\n
    【创建 Enum 类】\n
        - 导入 Enum 并创建继承自 str 和 Enum 的子类。 \n
        - 通过从 str 继承，API 文档就能把值的类型定义为字符串，并且能正确渲染。\n
        - 然后，创建包含固定值的类属性，这些固定值是可用的有效值\n

        AlexNet、ResNet、LeNet 是机器学习模型。\n
    '''
    # 比较枚举元素
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}
    # 获取枚举值 使用 model_name.value 获取实际的值（本例中为字符串）
    # 使用 ModelName.lenet.value 也能获取值 "lenet"。
    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    # 返回枚举元素

    # 即使嵌套在 JSON 请求体里（例如， dict），也可以从路径操作返回枚举元素。
    # 返回给客户端之前，要把枚举元素转换为对应的值（本例中为字符串）：

    return {"model_name": model_name, "message": "Have some residuals"}


# todo 4、包含路径的路径参数


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    '''
    【包含路径的路径参数】

        假设路径操作的路径为 /files/{file_path}。
        但需要 file_path 中也包含路径，比如，home/johndoe/myfile.txt。
        此时，该文件的 URL 是这样的：/files/home/johndoe/myfile.txt。


    【OpenAPI 支持】
        - OpenAPI 不支持声明包含路径的路径参数，因为这会导致测试和定义更加困难。
        - 不过，仍可使用 Starlette 内置工具在 FastAPI 中实现这一功能。
        - 而且不影响文档正常运行，但是不会添加该参数包含路径的说明。
    【路径转换器】
        直接使用 Starlette 的选项声明包含路径的路径参数：
        /files/{file_path:path}
        本例中，参数名为 file_path，结尾部分的 :path 说明该参数应匹配路径。

    【注意】
        包含 /home/johndoe/myfile.txt 的路径参数要以斜杠（/）开头。
        如果 URL 是 /files//home/johndoe/myfile.txt。注意，files 和 home 之间要使用双斜杠（//）。
    '''

    return {"file_path": file_path}


'''
【小结】
通过简短、直观的 Python 标准类型声明，FastAPI 可以获得：

 - 编辑器支持：错误检查，代码自动补全等
 - 数据解析
 - 数据校验
 - API 注解和 API 文档
只需要声明一次即可。
这可能是除了性能以外，FastAPI 与其它框架相比的主要优势。
'''
